import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { produce } from 'immer';
import { cloneDeep } from 'lodash';
import { nanoid } from '@reduxjs/toolkit';
import { ComponentPropsType } from '@/components/question';
import { arrayMove } from '@dnd-kit/sortable';
import { getNextSelectedId, insertNewComponent } from './utils';

export type ComponentInfoType = {
  fe_id: string;
  type: string;
  title: string;
  isHidden?: boolean;
  isLocked?: boolean;
  props: ComponentPropsType;
};

export type ComponentsStateType = {
  selectedId: string;
  componentList: ComponentInfoType[];
  copiedComponent: ComponentInfoType | null;
};

const INIT_STATE: ComponentsStateType = {
  selectedId: '',
  componentList: [],
  copiedComponent: null,
};

export const componentsSlice = createSlice({
  name: 'components',
  initialState: INIT_STATE,
  reducers: {
    resetComponents: (
      state: ComponentsStateType,
      action: PayloadAction<ComponentsStateType>
    ) => {
      return action.payload;
    },
    changeSelectedId: produce(
      (draft: ComponentsStateType, action: PayloadAction<string>) => {
        draft.selectedId = action.payload;
      }
    ),
    addComponent: produce(
      (
        draft: ComponentsStateType,
        action: PayloadAction<ComponentInfoType>
      ) => {
        const newComponent = action.payload;
        insertNewComponent(draft, newComponent);
      }
    ),
    changeComponentProps: produce(
      (
        draft: ComponentsStateType,
        action: PayloadAction<{ fe_id: string; newProps: ComponentPropsType }>
      ) => {
        const { fe_id, newProps } = action.payload;
        const target = draft.componentList.find((c) => c.fe_id === fe_id);
        if (target) {
          target.props = {
            ...target.props,
            ...newProps,
          };
        }
      }
    ),
    removeSelectedComponent: produce((draft: ComponentsStateType) => {
      const { selectedId, componentList } = draft;
      const newSelectedId = getNextSelectedId(selectedId, componentList);
      draft.selectedId = newSelectedId;
      const index = componentList.findIndex((c) => c.fe_id === selectedId);
      componentList.splice(index, 1);
    }),
    changeComponentIsHidden: produce(
      (
        draft: ComponentsStateType,
        action: PayloadAction<{ fe_id: string; isHidden: boolean }>
      ) => {
        const { componentList } = draft;
        const { fe_id, isHidden } = action.payload;
        let newSelectedId = '';
        if (isHidden) {
          newSelectedId = getNextSelectedId(fe_id, componentList);
        } else {
          newSelectedId = fe_id;
        }
        draft.selectedId = newSelectedId;
        const curComponent = componentList.find((c) => c.fe_id === fe_id);
        if (curComponent) curComponent.isHidden = isHidden;
      }
    ),
    toggleComponentIsLocked: produce(
      (
        draft: ComponentsStateType,
        action: PayloadAction<{ fe_id: string }>
      ) => {
        const { fe_id } = action.payload;
        const curComponent = draft.componentList.find((c) => c.fe_id === fe_id);
        if (curComponent) curComponent.isLocked = !curComponent.isLocked;
      }
    ),
    copySelectedComponent: produce((draft: ComponentsStateType) => {
      const { selectedId, componentList } = draft;
      const curComponent = componentList.find((c) => c.fe_id === selectedId);
      if (curComponent) {
        draft.copiedComponent = cloneDeep(curComponent);
      }
    }),
    pasteCopiedComponent: produce((draft: ComponentsStateType) => {
      const { copiedComponent } = draft;
      if (copiedComponent) {
        copiedComponent.fe_id = nanoid();
        insertNewComponent(draft, copiedComponent);
      }
    }),
    selectPrevComponent: produce((draft: ComponentsStateType) => {
      const { selectedId, componentList } = draft;
      const index = componentList.findIndex((c) => c.fe_id === selectedId);
      if (index > 0) {
        draft.selectedId = componentList[index - 1].fe_id;
      }
    }),
    selectNextComponent: produce((draft: ComponentsStateType) => {
      const { selectedId, componentList } = draft;
      const index = componentList.findIndex((c) => c.fe_id === selectedId);
      if (index < componentList.length - 1) {
        draft.selectedId = componentList[index + 1].fe_id;
      }
    }),
    changeComponentTitle: produce(
      (
        draft: ComponentsStateType,
        action: PayloadAction<{ fe_id: string; title: string }>
      ) => {
        const { fe_id, title } = action.payload;
        const component = draft.componentList.find((c) => c.fe_id === fe_id);
        if (component) component.title = title;
      }
    ),
    moveComponent: produce(
      (
        draft: ComponentsStateType,
        action: PayloadAction<{ oldIndex: number; newIndex: number }>
      ) => {
        const { oldIndex, newIndex } = action.payload;
        const { componentList: curComponentList } = draft;
        draft.componentList = arrayMove(curComponentList, oldIndex, newIndex);
      }
    ),
  },
});

export const {
  resetComponents,
  changeSelectedId,
  addComponent,
  changeComponentProps,
  removeSelectedComponent,
  changeComponentIsHidden,
  toggleComponentIsLocked,
  copySelectedComponent,
  pasteCopiedComponent,
  selectPrevComponent,
  selectNextComponent,
  changeComponentTitle,
  moveComponent,
} = componentsSlice.actions;
export default componentsSlice.reducer;
